原題:
https://cses.fi/problemset/task/1130
題意:
給一棵 n 點無根樹,選一些邊,使得任兩條選中的邊不共享端點(匹配)。求可選邊數最大值。
解題思路:
#include <bits/stdc++.h>
using namespace std;
const int INF_NEG = -1e9;
int n;
vector<vector<int>> g;
vector<array<int,2>> dp; // dp[u][0/1]
void dfs(int u, int p){
    int base = 0;
    for(int v: g[u]) if(v!=p){
        dfs(v, u);
        base += max(dp[v][0], dp[v][1]);
    }
    dp[u][0] = base;
    int bestGain = INF_NEG;
    for(int v: g[u]) if(v!=p){
        int gain = dp[v][0] + 1 - max(dp[v][0], dp[v][1]);
        bestGain = max(bestGain, gain);
    }
    dp[u][1] = (bestGain==INF_NEG ? INF_NEG : base + bestGain);
}
int main(){
    ios::sync_with_stdio(false); cin.tie(nullptr);
    cin >> n;
    g.assign(n+1, {});
    for(int i=0;i<n-1;i++){
        int a,b; cin>>a>>b;
        g[a].push_back(b); g[b].push_back(a);
    }
    dp.assign(n+1, {0, INF_NEG});
    dfs(1, 0);
    cout << max(dp[1][0], dp[1][1]) << "\n";
    return 0;
}
原題:
https://cses.fi/problemset/task/1133
題意:
給一棵 n 點樹,對每個節點 u,輸出 sum_{v} dist(u, v)。
解題思路(換根 DP)
#include <bits/stdc++.h>
using namespace std;
int n;
vector<vector<int>> g;
vector<long long> ans;
vector<int> sz;
void dfs1(int u, int p, int depth, long long &sumDepth){
    sz[u] = 1;
    sumDepth += depth;
    for(int v: g[u]) if(v!=p){
        dfs1(v, u, depth+1, sumDepth);
        sz[u] += sz[v];
    }
}
void dfs2(int u, int p){
    for(int v: g[u]) if(v!=p){
        // reroot: move root from u to v
        ans[v] = ans[u] + (long long)n - 2LL*sz[v];
        dfs2(v, u);
    }
}
int main(){
    ios::sync_with_stdio(false); cin.tie(nullptr);
    cin>>n;
    g.assign(n+1, {});
    for(int i=0;i<n-1;i++){
        int a,b; cin>>a>>b;
        g[a].push_back(b); g[b].push_back(a);
    }
    ans.assign(n+1, 0);
    sz.assign(n+1, 0);
    long long sumDepth = 0;
    dfs1(1, 0, 0, sumDepth);
    ans[1] = sumDepth;
    dfs2(1, 0);
    for(int i=1;i<=n;i++){
        cout << ans[i] << (i==n?'\n':' ');
    }
    return 0;
}
原題:
https://leetcode.com/problems/house-robber-iii/description/
題意:
二元樹,每個節點有金額。不能同時搶相鄰節點(父子不能同取)。求最大總額。
解題思路
/**
 * Definition for a binary tree node.
 * struct TreeNode { int val; TreeNode *left; TreeNode *right;
 *   TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *   TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *   TreeNode(int x, TreeNode *l, TreeNode *r) : val(x), left(l), right(r) {}
 * };
 */
class Solution {
    pair<int,int> dfs(TreeNode* u){
        if(!u) return {0,0}; // {notTake, take}
        auto L = dfs(u->left);
        auto R = dfs(u->right);
        int take = u->val + L.first + R.first;
        int notTake = max(L.first, L.second) + max(R.first, R.second);
        return {notTake, take};
    }
public:
    int rob(TreeNode* root) {
        auto [a,b] = dfs(root);
        return max(a,b);
    }
};
原題:
https://leetcode.com/problems/binary-tree-maximum-path-sum/
題意:
二元樹中任意兩節點之間的路徑和最大值(路徑必須連續向下或經過某節點轉彎;可為單點)。
解題思路:
class Solution {
    int best = INT_MIN;
    int gain(TreeNode* u){
        if(!u) return 0;
        int L = max(0, gain(u->left));
        int R = max(0, gain(u->right));
        best = max(best, u->val + L + R);            // 經過 u 作為峰頂
        return u->val + max(L, R);                   // 向上一條鏈
    }
public:
    int maxPathSum(TreeNode* root) {
        gain(root);
        return best;
    }
};